﻿using Cinch;
using FR.SAFTAnalyser.Model;
using FR.SAFTAnalyser.SAFT;
using MEFedMVVM.Common;
using OfficeOpenXml;
using OfficeOpenXml.Table;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Controls;

namespace FR.SAFTAnalyser.ViewModels
{
    public class InvoicesViewModel : WorkspaceViewModel
    {
        public InvoicesViewModel()
        {
            if (!Designer.IsInDesignMode)
            {
                Mediator.Instance.Register(this);
                this.DisplayName = "Documentos";

                DoOpenExcelCommand = new SimpleCommand<object, object>(CanOpenExcel, OpenExcel);
            }
        }

        #region Mediator messages
        [MediatorMessageSink(MessageType.SAFT_FILE_OPENED)]
        private void LoadInvoices(AuditFile saft)
        {
            this.SalesInvoices = saft.SourceDocuments.SalesInvoices;
        }

        [MediatorMessageSink(MessageType.INVOICE_LIST_CHANGED)]
        private void ReloadInvoices(AuditFile saft)
        {
            this.SalesInvoices = saft.SourceDocuments.SalesInvoices;
        }

        [MediatorMessageSink(MessageType.FOCUS_INVOICE)]
        private void FocusInvoice(string pk)
        {
            if (!string.IsNullOrEmpty(pk) && Invoices != null)
            {
                var invoicesWithError = from i in Invoices
                                        where i.Pk.AreEqualIgnoreCase(pk)
                                        select i;

                if (invoicesWithError != null)
                    CurrentInvoice = invoicesWithError.FirstOrDefault();
            }
        }

        [MediatorMessageSink(MessageType.FOCUS_INVOICE_LINE)]
        private void FocusInvoiceLine(string pk)
        {
            if (!string.IsNullOrEmpty(pk) && Invoices != null)
            {
                string[] pks = pk.Split(';');

                if (pks != null && pks.Length == 2)
                {
                    var invoicesWithError = from i in Invoices
                                            where i.Pk.AreEqualIgnoreCase(pks[1])
                                            select i;

                    if (invoicesWithError != null)
                    {
                        CurrentInvoice = invoicesWithError.FirstOrDefault();

                        if (currentInvoice != null)
                        {
                            var invoiceLineWithError = from l in CurrentInvoice.Line
                                                       where l.Pk.AreEqualIgnoreCase(pks[0])
                                                       select l;

                            if (invoiceLineWithError != null)
                                CurrentInvoiceLine = invoiceLineWithError.FirstOrDefault();

                        }
                    }
                }
            }
        }
        #endregion

        #region Binding properties
        SourceDocumentsSalesInvoices salesInvoices;
        public SourceDocumentsSalesInvoices SalesInvoices
        {
            get
            {
                if (salesInvoices == null && SAFT.SaftAnalyser.Instance.SaftFile != null)
                    salesInvoices = SAFT.SaftAnalyser.Instance.SaftFile.SourceDocuments.SalesInvoices;

                return salesInvoices;
            }
            set
            {
                salesInvoices = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.SalesInvoices));

                if (salesInvoices != null && string.IsNullOrEmpty(this.FilterInvoices))
                {
                    DocNumberOfEntries = salesInvoices.Invoice.Length;
                    DocTotalCredit = salesInvoices.Invoice
                                             .Where(c => c.DocumentStatus.InvoiceStatus !=  InvoiceStatus.A && c.DocumentStatus.InvoiceStatus != InvoiceStatus.F)
                                             .Sum(c => c.Line.Sum(l => l.ItemElementName == ItemChoiceType4.CreditAmount ? Math.Round(l.Item, 2, MidpointRounding.AwayFromZero) : 0))
                                             .ToString("c");

                    DocTotalDebit = salesInvoices.Invoice
                                            .Where(c => c.DocumentStatus.InvoiceStatus != InvoiceStatus.A && c.DocumentStatus.InvoiceStatus != InvoiceStatus.F)
                                            .Sum(c => c.Line.Sum(l => l.ItemElementName == ItemChoiceType4.DebitAmount ? Math.Round(l.Item, 2, MidpointRounding.AwayFromZero) : 0))
                                            .ToString("c");

                    NumberOfEntries = salesInvoices.NumberOfEntries;
                    TotalCredit = salesInvoices.TotalCredit.ToString("c");
                    TotalDebit = salesInvoices.TotalDebit.ToString("c");

                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.DocNumberOfEntries));
                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.DocTotalCredit));
                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.DocTotalDebit));

                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.NumberOfEntries));
                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.TotalCredit));
                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.TotalDebit));

                    this.Invoices = salesInvoices.Invoice;
                }
            }
        }

        SourceDocumentsSalesInvoicesInvoice[] invoices;
        public SourceDocumentsSalesInvoicesInvoice[] Invoices
        {
            get { return invoices; }
            set
            {
                invoices = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.Invoices));
            }
        }

        SourceDocumentsSalesInvoicesInvoice currentInvoice;
        public SourceDocumentsSalesInvoicesInvoice CurrentInvoice
        {
            get { return currentInvoice; }
            set
            {
                currentInvoice = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.CurrentInvoice));

                if (currentInvoice != null && string.IsNullOrEmpty(this.FilterLines))
                {
                    DocGrossTotal = currentInvoice.Line.Sum(c => c.UnitPrice * (1 + c.Tax.Item * 0.01m) * c.Quantity * Operation(currentInvoice, c)).ToString("c");
                    DocNetTotal = currentInvoice.Line.Sum(c => c.UnitPrice * c.Quantity * Operation(currentInvoice, c)).ToString("c");
                    DocTaxPayable = currentInvoice.Line.Sum(c => c.UnitPrice * c.Tax.Item * 0.01m * c.Quantity * Operation(currentInvoice, c)).ToString("c");

                    GrossTotal = currentInvoice.DocumentTotals.GrossTotal.ToString("c");
                    NetTotal = currentInvoice.DocumentTotals.NetTotal.ToString("c");
                    TaxPayable = currentInvoice.DocumentTotals.TaxPayable.ToString("c");

                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.DocGrossTotal));
                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.DocNetTotal));
                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.DocTaxPayable));

                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.GrossTotal));
                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.NetTotal));
                    NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.TaxPayable));

                    if (!this.ShowAllLines)
                        this.CurrentInvoiceLines = currentInvoice.Line;
                }
            }
        }

        SourceDocumentsSalesInvoicesInvoiceLine[] currentInvoiceLines;
        public SourceDocumentsSalesInvoicesInvoiceLine[] CurrentInvoiceLines
        {
            get { return currentInvoiceLines; }
            set
            {
                currentInvoiceLines = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.CurrentInvoiceLines));
            }
        }

        SourceDocumentsSalesInvoicesInvoiceLine currentInvoiceLine;
        public SourceDocumentsSalesInvoicesInvoiceLine CurrentInvoiceLine
        {
            get { return currentInvoiceLine; }
            set
            {
                currentInvoiceLine = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.CurrentInvoiceLine));
            }
        }

        bool showAllLines;
        public bool ShowAllLines
        {
            get { return showAllLines; }
            set
            {
                showAllLines = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<InvoicesViewModel>(x => x.ShowAllLines));

                if (showAllLines)
                    this.CurrentInvoiceLines = SalesInvoices.Invoice.SelectMany(i => i.Line).ToArray();
                else
                    this.CurrentInvoiceLines = CurrentInvoice.Line;
            }
        }

        string filterInvoices;
        public string FilterInvoices
        {
            get { return filterInvoices; }
            set
            {
                filterInvoices = value;

                if (string.IsNullOrEmpty(filterInvoices))
                    this.Invoices = SAFT.SaftAnalyser.Instance.SaftFile.SourceDocuments.SalesInvoices.Invoice;
                else
                {
                    var inv = from i in SAFT.SaftAnalyser.Instance.SaftFile.SourceDocuments.SalesInvoices.Invoice
                              where i.InvoiceNo.IndexOf(filterInvoices, StringComparison.OrdinalIgnoreCase) >= 0 ||
                                    i.InvoiceDate.ToString("yyyy-MM-dd").IndexOf(filterInvoices, StringComparison.OrdinalIgnoreCase) >= 0 ||
                                    i.SystemEntryDate.ToString("yyyy-MM-ddTHH:mm:ss").IndexOf(filterInvoices, StringComparison.OrdinalIgnoreCase) >= 0
                              select i;

                    this.Invoices = inv.ToArray();
                }
            }
        }

        string filterLines;
        public string FilterLines
        {
            get { return filterLines; }
            set
            {
                filterLines = value;

                if (string.IsNullOrEmpty(filterLines))
                    this.CurrentInvoiceLines = this.CurrentInvoice.Line;
                else
                {
                    var lines = from l in this.CurrentInvoice.Line
                                where l.ProductDescription.IndexOf(filterLines, StringComparison.OrdinalIgnoreCase) >= 0 ||
                                        l.ProductCode.IndexOf(filterLines, StringComparison.OrdinalIgnoreCase) >= 0 ||
                                        l.Description.IndexOf(filterLines, StringComparison.OrdinalIgnoreCase) >= 0
                                select l;

                    this.CurrentInvoiceLines = lines.ToArray();
                }
            }
        }

        public string DocGrossTotal { get; set; }

        public string DocNetTotal { get; set; }

        public string DocTaxPayable { get; set; }

        public int DocNumberOfEntries { get; set; }

        public string DocTotalCredit { get; set; }

        public string DocTotalDebit { get; set; }


        public string GrossTotal { get; set; }

        public string NetTotal { get; set; }

        public string TaxPayable { get; set; }

        public string NumberOfEntries { get; set; }

        public string TotalCredit { get; set; }

        public string TotalDebit { get; set; }
        #endregion

        #region Commands
        public SimpleCommand<object, object> DoOpenExcelCommand { get; private set; }

        private void OpenExcel(object args)
        {
            if (CurrentInvoice != null)
            {
                DataTable dtinvoice = new DataTable("Invoice");
                dtinvoice.Columns.Add("InvoiceNo", typeof(string));
                dtinvoice.Columns.Add("Estado", typeof(string));
                dtinvoice.Columns.Add("Data documento", typeof(DateTime));
                dtinvoice.Columns.Add("Data sistema", typeof(DateTime));
                dtinvoice.Columns.Add("Total s/ IVA", typeof(decimal));
                dtinvoice.Columns.Add("Total", typeof(decimal));
                dtinvoice.Columns.Add("Total imposto", typeof(decimal));
                dtinvoice.Columns.Add("Cliente", typeof(string));
                dtinvoice.Columns.Add("Periodo", typeof(int));
                dtinvoice.Columns.Add("Autofacturação", typeof(string));
                dtinvoice.Columns.Add("Transacção", typeof(string));
                dtinvoice.Columns.Add("Hash", typeof(string));
                dtinvoice.Columns.Add("Hash Test", typeof(string));
                dtinvoice.Columns.Add("Hash Control", typeof(string));

                dtinvoice.Rows.Add(
                    CurrentInvoice.InvoiceNo,
                    CurrentInvoice.DocumentStatus.InvoiceStatus.ToString(),
                    CurrentInvoice.InvoiceDate,
                    CurrentInvoice.SystemEntryDate,
                    CurrentInvoice.DocumentTotals.NetTotal,
                    CurrentInvoice.DocumentTotals.GrossTotal,
                    CurrentInvoice.DocumentTotals.TaxPayable,
                    CurrentInvoice.CustomerID,
                    Int32.Parse(CurrentInvoice.Period),
                    CurrentInvoice.SelfBillingIndicator,
                    CurrentInvoice.TransactionID,
                    CurrentInvoice.Hash,
                    CurrentInvoice.HashTest,
                    CurrentInvoice.HashControl);

                ExcelPackage pck = new ExcelPackage();
                var wsEnum = pck.Workbook.Worksheets.Add("SAFT Invoice");
                wsEnum.Cells["C2"].Style.Numberformat.Format = "yyyy-MM-dd hh:mm:ss";
                wsEnum.Cells["D2"].Style.Numberformat.Format = "yyyy-MM-dd hh:mm:ss";

                wsEnum.Cells["E2"].Style.Numberformat.Format = @"#,##0.00";
                wsEnum.Cells["F2"].Style.Numberformat.Format = @"#,##0.00";
                wsEnum.Cells["G2"].Style.Numberformat.Format = @"#,##0.00";
                //Load the collection starting from cell A1...
                wsEnum.Cells["A1"].LoadFromDataTable(dtinvoice, true, TableStyles.Medium9);
                wsEnum.Cells[wsEnum.Dimension.Address].AutoFitColumns();

                if (CurrentInvoiceLines != null)
                {
                    DataTable dtlines = new DataTable("InvoiceLines");

                    dtlines.Columns.Add("Nº linha", typeof(int));
                    dtlines.Columns.Add("Código produto", typeof(string));
                    dtlines.Columns.Add("Descrição produto", typeof(string));
                    dtlines.Columns.Add("Quantidade", typeof(decimal));
                    dtlines.Columns.Add("Preço unitário", typeof(decimal));
                    dtlines.Columns.Add("Valor", typeof(decimal));
                    dtlines.Columns.Add("Tipo operação", typeof(string));
                    dtlines.Columns.Add("Imposto", typeof(decimal));
                    dtlines.Columns.Add("Isenção imposto", typeof(string));
                    dtlines.Columns.Add("Unidade medida", typeof(string));
                    dtlines.Columns.Add("Data", typeof(DateTime));
                    dtlines.Columns.Add("Descrição", typeof(string));

                    foreach (var line in CurrentInvoiceLines)
                    {
                        dtlines.Rows.Add(
                            line.LineNumber,
                            line.ProductCode,
                            line.ProductDescription,
                            line.Quantity,
                            line.UnitPrice,
                            line.Item,
                            line.ItemElementName.ToString(),
                            line.Tax.Item,
                            line.TaxExemptionReason,
                            line.UnitOfMeasure,
                            line.TaxPointDate,
                            line.Description);
                    }

                    var wsEnumLines = pck.Workbook.Worksheets.Add("SAFT Invoice Lines");
                    wsEnumLines.Cells[string.Format("k2:k{0}", CurrentInvoiceLines.Length + 1)].Style.Numberformat.Format = "yyyy-MM-dd hh:mm:ss";

                    wsEnumLines.Cells[string.Format("D2:D{0}", CurrentInvoiceLines.Length + 1)].Style.Numberformat.Format = @"#,##0.00";
                    wsEnumLines.Cells[string.Format("E2:E{0}", CurrentInvoiceLines.Length + 1)].Style.Numberformat.Format = @"#,##0.00";
                    wsEnumLines.Cells[string.Format("F2:F{0}", CurrentInvoiceLines.Length + 1)].Style.Numberformat.Format = @"#,##0.00";
                    //Load the collection starting from cell A1...
                    wsEnumLines.Cells["A1"].LoadFromDataTable(dtlines, true, TableStyles.Medium9);
                    wsEnumLines.Cells[wsEnum.Dimension.Address].AutoFitColumns();
                }

                //...and save
                var fi = new FileInfo(Path.GetTempPath() + Path.GetRandomFileName() + ".xlsx");

                pck.SaveAs(fi);

                Process.Start(fi.FullName);
            }
        }

        private bool CanOpenExcel(object args)
        {
            if (CurrentInvoice != null)
                return true;
            else
                return false;
        }
        #endregion

        private int Operation(SourceDocumentsSalesInvoicesInvoice i, SourceDocumentsSalesInvoicesInvoiceLine l)
        {
            if (i.InvoiceType == InvoiceType.FT || i.InvoiceType == InvoiceType.VD || i.InvoiceType == InvoiceType.ND)
                return l.ItemElementName == ItemChoiceType4.CreditAmount ? 1 : -1;
            else if (i.InvoiceType == InvoiceType.NC)
                return l.ItemElementName == ItemChoiceType4.DebitAmount ? 1 : -1;

            return 1;
        }
    }
}
